---
layout: docs
page_title: Architecture - AWS ECS
description: >-
  Architecture of Consul Service Mesh on AWS ECS (Elastic Container Service).
---

# Architecture

The following diagram shows the main components of the Consul architecture when deployed to an ECS cluster:

![Consul on ECS Architecture](/img/consul-ecs-arch.png)

1. **Consul servers:** Production-ready Consul server cluster
1. **Application tasks:** Runs user application containers along with two helper containers:
   1. **Consul client:** The Consul client container runs Consul. The Consul client communicates
      with the Consul server and configures the Envoy proxy sidecar. This communication
      is called _control plane_ communication.
   1. **Sidecar proxy:** The sidecar proxy container runs [Envoy](https://envoyproxy.io/). All requests
      to and from the application container(s) run through the sidecar proxy. This communication
      is called _data plane_ communication.
1. **Mesh Init:** Each task runs a short-lived container, called `mesh-init`, which sets up initial configuration
   for Consul and Envoy.
1. **Health Syncing:** Optionally, an additional `health-sync` container can be included in a task to sync health statuses
   from ECS into Consul.
1. **ACL Controller:** The ACL controller is responsible for automating configuration and cleanup in the Consul servers.
   The ACL controller will automatically configure the [AWS IAM Auth Method](/docs/security/acl/auth-methods/aws-iam), and cleanup
   unused ACL tokens from Consul. When using Consul Enterprise namespaces, the ACL controller will automatically create Consul
   namespaces for ECS tasks.

For more information about how Consul works in general, see Consul's [Architecture Overview](/docs/architecture).

## Task Startup

This diagram shows the timeline of a task starting up and all its containers:

<ImageConfig width={400}>

![Task Startup Timeline](/img/ecs-task-startup.svg)

</ImageConfig>

- **T0:** ECS starts the task. The `consul-client` and `mesh-init` containers start:
  - `consul-client` does the following:
    - If ACLs are enabled, a startup script runs a `consul login` command to obtain a
      token from the AWS IAM auth method for the Consul client. This token has `node:write`
      permissions.
    - It uses the `retry-join` option to join the Consul cluster.
  - `mesh-init` does the following:
    - If ACLs are enabled, mesh-init runs a `consul login` command to obtain a token from
      the AWS IAM auth method for the service registration. This token has `service:write`
      permissions for the service and its sidecar proxy. This token is written to a shared
      volume for use by the `health-sync` container.
    - It registers the service for the current task and its sidecar proxy with Consul.
    - It runs `consul connect envoy -bootstrap` to generate Envoy’s bootstrap JSON file and
      writes it to a shared volume.
- **T1:** The following containers start:
  - `sidecar-proxy` starts using a custom entrypoint command, `consul-ecs envoy-entrypoint`.
     The entrypoint command starts Envoy by running `envoy -c <path-to-bootstrap-json>`.
  - `health-sync` starts if ECS health checks are defined or if ACLs are enabled. It syncs health
    checks from ECS to Consul (see [ECS Health Check Syncing](#ecs-health-check-syncing)).
- **T2:** The `sidecar-proxy` container is marked as healthy by ECS. It uses a health check that
  detects if its public listener port is open. At this time, your application containers are started
  since all Consul machinery is ready to service requests.

## Task Shutdown

This diagram shows an example timeline of a task shutting down:

<ImageConfig width={400}>

![Task Shutdown Timeline](/img/ecs-task-shutdown.svg)

</ImageConfig>

- **T0**: ECS sends a TERM signal to all containers. Each container reacts to the TERM signal:
  - `consul-client` begins to gracefully leave the Consul cluster.
  - `health-sync` stops syncing health status from ECS into Consul checks.
  - `sidecar-proxy` ignores the TERM signal and continues running until the `user-app` container
    exits. The custom entrypoint command, `consul-ecs envoy-entrypoint`, monitors the local ECS task
    metadata. It waits until the `user-app` container has exited before terminating Envoy. This
    enables the application to continue making outgoing requests through the proxy to the mesh for
    graceful shutdown.
  - `user-app` exits if it is not configured to ignore the TERM signal. The `user-app` container
    will continue running if it is configured to ignore the TERM signal.
- **T1**:
  - `health-sync` does the following:
    - It updates its Consul checks to critical status and exits. This ensures this service instance is marked unhealthy.
    - If ACLs are enabled, it runs `consul logout` for the two tokens created by the `consul-client` and `mesh-init` containers.
      This removes those tokens from Consul. If `consul logout` fails for some reason, the ACL controller will remove the tokens
      after the task has stopped.
  - `sidecar-proxy` notices the `user-app` container has stopped and exits.
- **T2**: `consul-client` finishes gracefully leaving the Consul datacenter and exits.
- **T3**:
  - ECS notices all containers have exited, and will soon change the Task status to `STOPPED`
  - Updates about this task have reached the rest of the Consul cluster, so downstream proxies have been updated to stopped sending traffic to this task.
- **T4**: At this point task shutdown should be complete. Otherwise, ECS will send a KILL signal to any containers still running. The KILL signal cannot be ignored and will forcefully stop containers. This will interrupt in-progress operations and possibly cause errors.

## ACL Tokens

Two types of ACL tokens are required by ECS tasks:

* **Client tokens:** used by the `consul-client` containers to join the Consul cluster
* **Service tokens:** used by sidecar containers for service registration and health syncing

With Consul on ECS, these tokens are obtained dynamically when a task starts up by logging
in via Consul's AWS IAM auth method.

### Consul Client Token

Consul client tokens require `node:write` for any node name, which is necessary because the Consul node
names on ECS are not known until runtime.

### Service Token

Service tokens are associated with a [service identity](/docs/security/acl#service-identities).
The service identity includes `service:write` permissions for the service and sidecar proxy.

## AWS IAM Auth Method

Consul's [AWS IAM Auth Method](/docs/security/acl/auth-methods/aws-iam) is used by ECS tasks to
automatically obtain Consul ACL tokens. When a service mesh task on ECS starts up, it runs two
`consul login` commands to obtain a client token and a service token via the auth method. When the
task stops, it attempts two `consul logout` commands in order to destroy these tokens.

During a `consul login`, the [task's IAM
role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) is presented
to the AWS IAM auth method on the Consul servers. The role is validated with AWS. If the role is
valid, and if the auth method trusts the IAM role, then the role is permitted to login. A new Consul
ACL token is created and [Binding Rules](/docs/security/acl/auth-methods#binding-rules) associate
permissions with the newly created token. These permissions are mapped to the token based on the IAM
role details. For example, tags on the IAM role are used to specify the service name and the
Consul Enterprise namespace to be associated with a service token that is created by a successful
login to the auth method.

### Task IAM Role

The following configuration is required for the task IAM role in order to be compatible with the
auth method. When using Terraform, the `mesh-task` module creates the task role with this
configuration by default.

* A scoped `iam:GetRole` permission must be included on the IAM role, enabling the role to fetch
  details about itself.
* A `consul.hashicorp.com.service-name` tag on the IAM role must be set to the Consul service name.
* <EnterpriseAlert inline /> A <code>consul.hashicorp.com.namespace</code> tag must be set on the
  IAM role to the Consul Enterprise namespace of the Consul service for the task.

Task IAM roles should not typically be shared across task families. Since a task family represents a
single Consul service, and since the task role must include the Consul service name, one task role
is required for each task family when using the auth method.

### Security

The auth method relies on the configuration of AWS resources, such as IAM roles, IAM policies, and
ECS tasks. If these AWS resources are misconfigured or if the account has loose access controls,
then the security of your service mesh may be at risk.

Any entity in your AWS account with the ability to obtain credentials for an IAM role could potentially
obtain a Consul ACL token and impersonate a Consul service. The `mesh-task` Terraform module
mitigates against this concern by creating the task role with an `AssumeRolePolicyDocument` that
allows only the AWS ECS service to assume the task role. By default, other entities are unable
to obtain credentials for task roles, and are unable to abuse the AWS IAM auth method to obtain
Consul ACL tokens.

However, other entities in your AWS account with the ability to create or modify IAM roles can
potentially circumvent this. For example, if they are able to create an IAM role with the correct
tags, they can obtain a Consul ACL token for any service. Or, if they can pass a role to an ECS task
and start an ECS task, they can use the task to obtain a Consul ACL token via the auth method.

The IAM policy actions `iam:CreateRole`, `iam:TagRole`, `iam:PassRole`, and `sts:AssumeRole` can be
used to restrict these capabilities in your AWS account and improve security when using the AWS IAM
auth method. See the [AWS
documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) to learn how
to restrict these permissions in your AWS account.

## ACL Controller

The ACL controller performs the following operations on the Consul servers:

* Configures the Consul AWS IAM auth method.
* Monitors tasks in ECS cluster where the controller is running.
* Cleans up unused Consul ACL tokens created by tasks in this cluster.
* <EnterpriseAlert inline /> Manages Consul admin partitions and namespaces.

### Auth Method Configuration

The ACL controller is responsible for configuring the AWS IAM auth method. The following resources
are created by the ACL controller when it starts up:

* **Client role**: The controller creates the Consul (not IAM) role and policy used for client
  tokens if these do not exist. This policy has `node:write` permissions to enable Consul clients to
  join the Consul cluster.
* **Auth method for client tokens**: One instance of the AWS IAM auth method is created for client
  tokens, if it does not exist. A binding rule is configured that attaches the Consul client role to each
  token created during a successful login to this auth method instance.
* **Auth method for service tokens**: One instance of the AWS IAM auth method is created for service
  tokens, if it does not exist:
  * A binding rule is configured to attach a [service identity](/docs/security/acl#service-identities)
    to each token created during a successful login to this auth method instance. The service name for
    this service identity is taken from the tag, `consul.hashicorp.com.service-name`, on the IAM role
    used to log in.
  * <EnterpriseAlert inline /> A namespace binding rule is configured to create service tokens in
    the namespace specified by the tag, <code>consul.hashicorp.com.namespace</code>, on the IAM
    role used to log in.

The ACL controller configures both instances of the auth method to permit only certain IAM roles to login,
by setting the [`BoundIAMPrincipalARNs`](/docs/security/acl/auth-methods/aws-iam#boundiamprincipalarns)
field of the AWS IAM auth method as follows:

* By default, the only IAM roles permitted to log in must have an ARN matching the pattern,
  `arn:aws:iam::<ACCOUNT>:role/consul-ecs/*`. This allows IAM roles at the role path `/consul-ecs/`
  to log in, and only those IAM roles in the same AWS account where the ACL controller is running.
* The role path can be changed by setting the `iam_role_path` input variable for the `mesh-task` and
  `acl-controller` modules, or by passing the `-iam-role-path` flag to the `consul-ecs
  acl-controller` command.
* Each instance of the auth method is shared by ACL controllers in the same Consul datacenter. Each
  controller updates the auth method, if necessary, to include additional entries in the
  `BoundIAMPrincipalARNs` list. This enables the use of the auth method with ECS clusters in
  different AWS accounts, for example. This does not apply when using Consul Enterprise admin
  partitions because auth method instances are not shared by multiple controllers in that case.

### Task Monitoring

After startup, the ACL controller monitors tasks in the same ECS cluster where the ACL controller is
running in order to discover newly running tasks and tasks that have stopped.

The ACL controller cleans up tokens created by `consul login` for tasks that are no longer running.
Normally, each task attempts `consul logout` commands when the task stops to destroy its tokens.
However, in unstable conditions the `consul logout` command may fail to clean up a token.
The ACL controller runs continually to ensure those unused tokens are soon removed.

### Admin Partitions and Namespaces<EnterpriseAlert inline />

When [admin partitions and namespaces](/docs/ecs/enterprise#admin-partitions-and-namespaces) are enabled,
the ACL controller is assigned to its configured admin partition. It supports one ACL controller instance per ECS
cluster. This results in an architecture with one admin partition per ECS cluster.

When admin partitions and namespace are enabled, the ACL controller performs the following
additional actions:

* At startup, creates its assigned admin partition if it does not exist.
* Inspects task tags for new ECS tasks to discover the task's intended partition
  and namespace. The ACL controller ignores tasks with a partition tag that does not match the
  controller's assigned partition.
* Creates namespaces when tasks start up. Namespaces are only created if they do not exist.
* Creates auth method instances for client and service tokens in controller's assigned admin partition.

## ECS Health Check Syncing

If the following conditions apply, ECS health checks automatically sync with Consul health checks for all application containers:

* marked as `essential`
* have ECS `healthChecks`
* are not configured with native Consul health checks

The `mesh-init` container creates a TTL health check for every container that fits these criteria
and the `health-sync` container ensures that the ECS and Consul health checks remain in sync.
